/*
 * Copyright 2022 André Claßen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.burnoutcrew.reorderable

import androidx.compose.foundation.gestures.awaitDragOrCancellation
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.awaitLongPressOrCancellation
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow

/**
 * 检测拖拽手势并触发重新排序操作的修饰符
 *
 * 此修饰符会在用户触摸并开始拖动时立即触发重新排序操作，
 * 适用于需要即时响应拖拽的场景
 *
 * @param state 重新排序状态对象，用于管理整个重新排序过程的状态
 * @return 应用了拖拽检测逻辑的Modifier
 */
fun Modifier.detectReorder(state: ReorderableState<*>) = detect(state){
    awaitDragOrCancellation(it)
}

/**
 * 检测长按后拖拽手势并触发重新排序操作的修饰符
 *
 * 此修饰符会在用户长按项目后再开始拖动时触发重新排序操作，
 * 适用于需要区分点击和拖拽操作的场景
 *
 * @param state 重新排序状态对象，用于管理整个重新排序过程的状态
 * @return 应用了长按拖拽检测逻辑的Modifier
 */
fun Modifier.detectReorderAfterLongPress(state: ReorderableState<*>) = detect(state) {
    awaitLongPressOrCancellation(it)
}

/**
 * 通用的拖拽检测修饰符构建器
 *
 * 这是一个内部函数，用于构建不同类型的拖拽检测逻辑。
 * 它会监听用户的触摸事件，并根据提供的检测策略来确定何时开始拖拽操作。
 *
 * 工作流程：
 * 1. 使用 `onGloballyPositioned` 监听组件在屏幕中的位置
 * 2. 使用 `pointerInput` 监听触摸手势
 * 3. 在每个手势序列中等待第一次按下
 * 4. 使用传入的检测策略（如立即拖拽或长按后拖拽）来确定起始点
 * 5. 如果检测成功，则计算相对于布局的位置并通知状态对象开始拖拽
 *
 * @param state 重新排序状态对象
 * @param detect 拖拽检测策略函数，决定何时认为拖拽开始
 * @return 应用了拖拽检测逻辑的Modifier
 */
private fun Modifier.detect(state: ReorderableState<*>, detect: suspend AwaitPointerEventScope.(PointerId)->PointerInputChange?) = composed {

    // 记录当前项在窗口中的位置，用于计算相对坐标
    val itemPosition = remember { mutableStateOf(Offset.Zero) }

    Modifier.onGloballyPositioned {
        // 当组件布局发生变化时更新其在窗口中的位置
        itemPosition.value = it.positionInWindow()
    }.pointerInput(Unit) {
        // 监听所有的 pointer 事件
        awaitEachGesture {
            // 等待第一个手指按下事件
            val down = awaitFirstDown(requireUnconsumed = false)
            // 使用指定的检测策略来确定拖拽起始点
            val start = detect(down.id)

            // 如果成功检测到拖拽起始点
            if (start != null) {
                // 计算相对于可重组件布局的坐标位置
                // itemPosition.value: 当前项在窗口中的位置
                // state.layoutWindowPosition.value: 整个可重组件在窗口中的位置
                // start.position: 触摸点相对于当前项的位置
                val relativePosition = itemPosition.value - state.layoutWindowPosition.value + start.position
                // 通知状态对象开始拖拽操作
                state.onDragStart(relativePosition.x.toInt(), relativePosition.y.toInt())
            }
        }
    }
}